<?php
/*
 * (c) Runar Bergheim Olsen, ShadowsHome.net
 *
 * template.class.php
 *
 * Desc: 		This class was written to support template files to be used in webpages
 *
 * Start date: 	25. Sept 2006 15:20
 * Version:		3.0
 */
 
 /**
 * template.class.php contains the Template class
 *
 * @author Runar Olsen <shadow@shadowshome.net>
 * @copyright Copyright &copy; 2006, Runar Olsen
 * @version 3.0
 * @filesource
 */

/**
 * Template class
 *
 * This class was made to support template files in a PHP Project.
 * 
 * Examples:
 * <code>
 * <!-- template.tpl -->
 * <html>
 * <head>
 * <title>{title}</title>
 * 
 * </head>
 * <body>
 * <!-- SWITCH switch1 -->
 * Switch1 is set to true :D<br />
 * <!-- END SWITCH switch1 -->
 * <!-- SWITCH switch2 -->
 * Switch2 is set to true :D<br />
 * <!-- END SWITCH switch2 -->
 * <br />
 * <table>
 * <tr><td>Name</td><td>Age</td></tr>
 * <!-- START testblock -->
 * <tr><td>{testblock.name}</td><td>{testblock.age}</td></tr>
 * <!-- END testblock -->
 * </table>
 * </body>
 * </html>
 * 
 * <?php
 * // test.php
 * $template = new Template();
 * 
 * 
 * // Let the template class know about the template file
 * $template->set_file_alias("test", "templates/testtemplate.tpl");
 * 
 * // Set the {title} tag
 * $template->assign_vars(array("title"=>"Template Example"));
 * 
 * // Set one switch to true (show) and one to false (hide)
 * $template->set_switch("switch1", true);
 * $template->set_switch("switch2", false);
 * 
 * // Add some block rows
 * $template->assign_block_vars("testblock", array("name"=>"Jon Doe", "age"=>15));
 * $template->assign_block_vars("testblock", array("name"=>"Runar", "age"=>15));
 * $template->assign_block_vars("testblock", array("name"=>"Ola Nordmann", "age"=>20));
 * 
 * // Echo the parsed template
 * echo $template->parse("test");
 * 
 * ?>
 * 
 * </code>
 * <pre>
 * Output:
 * 
 * Test switch is set to true
 * 
 * 
 *  ------------------------
 * | Name			|	Age	|
 * | Jon Doe		|	15	|
 * | Runar			| 	15	|
 * | Ola Nordmann	|	20	|
 *  ------------------------
 * </pre>
 * 
 * <pre>
 *  Change log:
 * 	Version 3.1 (20. Feb 2007)
 * 		Added a basic caching system. This sytem is still in beta stage and is due to change with
 * 		out a warning.
 * 		Also did a few bug fixed.
 *  Version 3 (13 Feb 15:56)
 *		Persing functions rewritten now using regexp instend of a million strpos substr functions.
 *		This has made the parsing faster and easyer to read.
 *		
 *		The logic behind the include function has changed abit. Before it was only allowing a file
 *		to be include once to prevent a loop of includes. Now it makes sure that a file can not 
 *		include the file(s) which include it. Ex:
 *			test.tpl includes test1.tpl
 *			test.tpl can at any point include test1.tpl again but test1.tpl can't include test.tpl
 *			test1.tpl includes test2.tpl
 *			test1.tpl can at any point include test2.tpl again and test.tpl can also include test2.tpl
 *			but test2 can't include test.tpl nor test1.tpl since they have include test2.tpl. 
 *			If the script did allow test2.tpl to do	a include of test.tpl or test1.tpl the script
 *			is locked in a loop looping untill the php time limit runs out.
 *
 *		The parsing function can now remove html comments from the result if requested. This makes
 *		the output smaller and it will remove any information you have in your tempalte files
 *		which the end user don't need. This feature is by default activated but can be disabled
 * 		by calling Tempalte::setStripHtmlComments(false);
 *  Version 2.3 (11 Feb 14:04)
 *		Addressed the error with blocks and switches without a end tag. Also addressed
 *		a problem in the include parsing function which whould cause an infinity loop
 *		if two templates included eachother.
 *		Added support for filealiases in the include tags.
 *		Added support for break tags which will make it possible to parse only a part 
 *		of a template file. May be usefull if you need to print one part before the other.
 *  Version 2.2 (10 Feb 17:49)
 *		Support for <!-- INCLUDE filename --> has been added on request. Also made the
 *		class a bit closer the the PEAR standar.
 *
 *		TODO: If there is a start switch or block tag without a end tag the class is locked
 *            in a infinity loop. Top prior.
 *  Version 2.1 (8 Jan 21:45)
 *		Fixed bug which limited the block and switch names to uppercase, if you had a 
 *      lower case block or swtich the class got locked in a infinity loop.
 *
 *      TODO: If there is a start switch or block tag without a end tag the class is locked
 *            in a infinity loop. Top prior.
 *  Version 2.0 (27 Des 23:10)
 *		Parsing functions rewritten to address the missing block in block and switch
 *		in switch support. Also using strpos to find tags in a string and not reading
 *		the files into an array and then looping the array over and over again like before.
 *		This made it possible to put switch/block tags inside texts and not on a own line.
 *
 *		Comments were also added for autogenerating phpDocs
 *  Version 1.0
 * 		Class made.
 *   	TODO: Add support for a block in a block and a switch in a switch
 * </pre>
 * 
 *	@author Runar Olsen <shadow@shadowshome.net>
 *	@package ClassLibrary
 *	@since 1.0 
 * 	@version 3.1
 *
 */
 
class Template {
 
 	/**
	* Class version
	*
	* This is a static value containing the current version of the class.
	*
	* @var String
	* @staticvar
	*/
	var $VERSION 	= "3.1"; 									
	
	/**
	 * Block tag id
	 * 
	 * The id of the block tag, used by Template::change_tag
	 * 
	 * @var int
	 * @staticvar
	 * @access Private
	 */
	var $_BLOCK_TAG = 1;
	
	/**
	 * Switch tag id
	 * 
	 * The id of the switch tag, used by Template::change_tag
	 * 
	 * @var int
	 * @staticvar
	 * @access Private
	 */
	var $_SWITCH_TAG = 1;
	
	/**
	 * Break tag id
	 * 
	 * The id of the break tag, used by Template::change_tag
	 * 
	 * @var int
	 * @staticvar
	 * @access Private
	 */
	var $_BREAK_TAG = 1;
	
	/**
	 * Include tag id
	 * 
	 * The id of the include tag, used by Template::change_tag
	 * 
	 * @var int
	 * @staticvar
	 * @access Private
	 */
	var $_INCLUDE_TAG = 1;
	
	/**
	* Tag array
	*
	* An array which the class uses to save tags sent to it by Template::assign_vars().
	*
	* @var Array
	*/
	var $tags 		= array();									
	
	/**
	* Block tag array
	*
	* An array which the class uses to save block tags sent to it by Template::assign_block_vars().
	*
	* @var Array
	*/
	var $block_tags = array();									
																
    /**
	* File alias
	*
	* An array which the class uses to save file alias' sent to it by Template::set_file_alias().
	*
	* @var Array
	*/																
	var $files 		= array();									
	
	/**
	* Switch array
	*
	* An array which the class uses to save switches sent to it by Template::set_switch_vars().
	*
	* @var Array
	*/
	var $switches 	= array();									
	
	/**
	* Tag starter
	*
	* A string with the tag starter.
	*
	* @var String
	*/
	var $tag_start 	= "{";										
	
	/**
	* Tag ender
	*
	* A string with the tag ender.
	*
	* @var String
	*/
	var $tag_end	= "}";
	
	/**
	 * Strip html comments?
	 * 
	 * This will be set to true if you want the script to strip all html comments
	 * before returning the template.
	 * Doing this will make the output size smaller and save bandwidth.
	 * 
	 * @var boolean
	 * @access private
	 */
	var $_stripHtmlComments = true;
	
	/**
	* Break tag
	*
	* An array with the break tag.
	*
	* @var Array
	* @access Private
	*/
	var $_break	= array("<!-- BREAK", "-->");
	
	/**
	* Include tag
	*
	* An array with the include tag.
	*
	* @var Array
	* @access Private
	*/
	var $_include	= array("<!-- INCLUDE", "-->");
	
	/**
	* Block tags
	*
	* An array with the block tags.
	* 
	* START,STARTEND,END,ENDEND
	*
	* @var Array
	* @access Private
	*/
	var $_block	= array("<!-- START", "-->", "<!-- END", "-->");
	
	/**
	* Switch tags
	*
	* An array with the switch tags.
	* 
	* START,STARTEND,END,ENDEND
	*
	* @var Array
	* @access Private
	*/
	var $_switch	= array("<!-- SWITCH", "-->", "<!-- END SWITCH", "-->");
	
	
	/**
	* @ignore
	*/
	function template() {}
	
	/**
	* Sets a file alias
	*
	* This function will make a file alias based on the paraments
	*
	* @param string $filealias The file alias
	* @param string $filepath The file path (abolute/relavtive)
	*
	*/
	function set_file_alias($filealias, $filepath) 
	{
		if(is_file($filepath) and is_readable($filepath)) 
		{
			$this->files[$filealias] = $filepath;
		} else 
		{
			$debug = debug_backtrace();
			$debug = $debug[0];
			trigger_error("Template::set_file_alias(): Could not find template file '$filepath' in <b>{$debug["file"]}</b> on line <b>{$debug["line"]}</b>", E_USER_NOTICE);
		}
	}
	
	/**
	 * Strip HTML comments?
	 * 
	 * Set this to true if you want to strip HTML comments and false if not.
	 * Doing this will make the output size smaller and save bandwidth.
	 * Default is true
	 * 
	 * @param boolean $stripHtmlComments True/false
	 */
	function setStripHtmlComments($stripHtmlComments)
	{
		$this->_stripHtmlComments = $stripHtmlComments;
	}
	
	/**
	* Saves the passed tags
	*
	* This function will save the passed array of tags.
	*
	* @param Array $tags Tags to save
	* 
	* @deprecated Deprecated since version 2.0, use Template::assign_vars()
	*
	*/
	function set_tags($tags) 
	{
		$this->assign_vars($tags);
	}
	
	/**
	* Saves the passed variables
	*
	* This function will save the passed array of variables.
	*
	* @param array $vars Variables to save
	*
	*/
	function assign_vars($vars) 
	{
		if(sizeof($vars) > 0)
		{
			foreach($vars as $var => $replace) 
			{
				$this->tags[$var] = $replace;
			}
		} else {
			$debug = debug_backtrace();
			$debug = $debug[0];
			trigger_error("Template::assign_vars(): Tried to assign an empty array in <b>{$debug["file"]}</b> on line <b>{$debug["line"]}</b>", E_USER_NOTICE);
		}
	}
	
	/**
	* Saves the passed block tags
	*
	* This function will save the passed array of block tags.
	*
	* @param string $block The block the varriables belong to
	* @param array $tags Tags to save
	*
	* @deprecated Deprecated since version 2.0, use Template::assign_block_vars()
	*
	*/
	function set_block_tags($block, $tags) 
	{
	 	$this->assign_block_vars($block, $tags);
	}
	
	/**
	* Saves the passed block variables
	*
	* This function will save the passed array of block variables.
	*
	* @param string $block The name of the block
	* @param array $vars Variables to save
	*
	*/
	function assign_block_vars($block, $vars) 
	{
		$temp = "";
		if(strpos($block, ".") === false) {
			$this->block_tags[$block][] = $vars;	
		} else 
		{
			$block = explode(".", $block);
			foreach($block as $block_id => $block_name) 
			{
				if($block_id == (sizeof($block)-1)) 
				{
					$temp .= "[\"$block_name\"][]";
				} else 
				{
					$temp .= "[\"$block_name\"]";
					eval("\$no = sizeof(\$this->block_tags$temp)-1; ");
					$temp .= "[$no][\"_SUBBLOCKS_\"]";
				}
			}
			eval ("\$this->block_tags$temp = \$vars;");
		}
	}
	
	/**
	* Saves the passed switch variables
	*
	* This function will save the passed switch variable.
	*
	* @param string $switchname Switch variable name
	* @param boolean $switchvar Switch value
	* 
	* @deprecated Deprecated since version 3 use Template::assign_switch()
	* 
	*/
	function set_switch($switchname, $switchvar) 
	{
		$this->assign_switch($switchname, $switchvar);
	}
	
	/**
	* Saves the passed switch variables
	*
	* This function will save the passed switch variable.
	*
	* @param string $switchname Switch variable name
	* @param boolean $switchvar Switch value
	*
	*/
	function assign_switch($switchname, $switchvar) 
	{
		$this->switches[$switchname] = $switchvar;
	}
	
	/**
	 * Set tag
	 * 
	 * This function can be used to change the 
	 * BREAK, INCLUDE, SWITCH or BLOCK tags
	 * 
	 * @param int $tag The tag we're replacing, Template::_BLOCK_TAG, Template::_BREAK_TAG, Template::_INCLUDE_TAG or Template::_SWITCH_TAG
	 * @param array $newTags The new tags
	 */
	function change_tag($tag, $newTags)
	{
		switch ($tag) 
		{
			case $this->_BLOCK_TAG:
				if(sizeof($newTags) == 4)
				{
					$this->_break = $newTags;
				}
				break;
			case $this->_BLOCK_TAG:
				if(sizeof($newTags) == 2)
				{
					$this->_block = $newTags;
				}
				break;
				
			case $this->_INCLUDE_TAG:
				if(sizeof($newTags) == 2)
				{
					$this->_include = $newTags;
				}
				break;
			case $this->_SWITCH_TAG:
				if(sizeof($newTags) == 4)
				{
					$this->_switch = $newTags;
				}
				break;
			default:
				return false;
				break;
		}
		return true;
	}
	
	/**
	* Starts the parsing function
	*
	* This function will start the parsing functions.
	*
	* @param string $template The name of the file alias we're parsing
	* @param string $breakTag The name of the break tag to break at if needed
	* @param boolean $infrontOf Set to true if you want the text infront of the break tag, false if you want the text behind
	*
	* @return	string The finished text
	*
	*/
	function parse($template, $breakTag = false, $infrontOf = true, $cache = false, $leaveout = array()) 
	{
		if(isset($this->files[$template])) 
		{
			$text = implode("", file($this->files[$template]));
		} else {
			$text = $template;
		}
			
		if($breakTag) 
		{
			$pos = strpos($text, $this->_break[0]. " " . $breakTag . " " . $this->_break[1]);
			if($pos !== false)
			{
				if($infrontOf) 
					$text = substr($text, 0, $pos);
				 else 
					$text = substr($text, $pos+strlen($this->_break[0] . " " .  $breakTag . " " .  $this->_break[1]));					
			}
		}
		$text = $this->_parseIncludes($text, $template);
		$text = $this->_parseSwitch($text, $leaveout);
		$text = $this->_parseBlocks($text, $this->block_tags, $leaveout);	
		$text = $this->_parsePageTags($text, $leaveout);
		
		// This will clean all the unused breaktags from the text
		$text = preg_replace("/{$this->_break[0]} .*? {$this->_break[1]}/i", "", $text);
		
		if($this->_stripHtmlComments && !$cache)
			$text = preg_replace("/<!-- .*? -->/i", "", $text);		
			
		if(isset($this->_caches[$template]) && !$cache)
		{
			echo $breaktag;
			$this->_generateCache($this->_caches[$template]["name"], $template, $breakTag, $infrontOf);	
		}
		return $text;
	}
	
	/**
	 * Parses the include statements in the given text
	 * 
	 * This function will remove all include tags in the given file
	 * and replace it with the content of the file requested. The file 
	 * included can't include the file which included it. This is to prevent 
	 * a infinty loop. Also the current file can't include itself.
	 * 
	 * @param string $text The text to parse
	 * @param string $curFile The current file
	 * @param array $includePath This array will contain the chain of includes
	 * 							 done to make sure we don't get a loop
	 */
	function _parseIncludes($text, $curFile, $includePath = array()) 
	{
		if(isset($this->files[$curFile]))
			$curFile = $this->files[$curFile];
		$includePath[$curFile] = true;
		while(preg_match("/{$this->_include[0]} (.+?) {$this->_include[1]}/i", $text, $match))
		{
			$fileName = $match[1];
			$realFileName = $fileName;
			$fileText = "";
			if(isset($this->files[$fileName])) 
				$fileName = $this->files[$fileName];
			if(!isset($includePath[$fileName]))
			{
				if(is_file($fileName) && is_readable($fileName))
				{
					$fileText = implode("", file($fileName));
					$fileText = $this->_parseIncludes($fileText, $fileName, $includePath);
				}
			}
			$text = preg_replace("/{$this->_include[0]} $realFileName {$this->_include[1]}/i", $fileText, $text);
		}
		return $text;
	}
	
	/**
	* Parses the blocks in the given text
	*
	* This function will parse the blocks in the given text.
	* <b>This should only be called by Template::parse()</b>
	*
	* @param string $text The text to parse
	* @param array $blockarray An array with the block data
	* @param string	$blocklevel If we're in a sub block this should be set
	* @access private
	*
	*/
	function _parseBlocks($text, $blockarray, $leaveout, $blocklevel = "") 
	{
		while (preg_match("/{$this->_block[0]} (.+?) {$this->_block[1]}/i", $text, $match)) 
		{
			$blockTemp = "";
			$blockText = "";
			$blockName = $match[1];	
			if($blocklevel != "") 
			{
				$realBlockName = substr($blockName, strlen($blocklevel)+1);
			} else 
			{
				$realBlockName = $blockName;
			}
			if(preg_match("/{$this->_block[0]} $blockName {$this->_block[1]}(.*?){$this->_block[2]} $blockName {$this->_block[3]}/is", $text, $match) && isset($blockarray[$realBlockName])) {
				$blockText = $match[1];	
				if(in_array($realBlockName, $leaveout)) 
				{
					$blockText = preg_replace(array(
						"/{$this->_block[0]} /i",
						"/ {$this->_block[1]}/i",
						"/{$this->_block[2]} /i",
						"/ {$this->_block[3]}/i"
					), array(
						"{$this->_block[0]}-- ",
						" --{$this->_block[1]}",
						"{$this->_block[2]}-- ",
						" --{$this->_block[3]}"
					), $blockText);
					$text = preg_replace("/{$this->_block[0]} $blockName {$this->_block[1]}.*?{$this->_block[2]} $blockName {$this->_block[3]}/is",
							 "{$this->_block[0]}-- $blockName --{$this->_block[1]}$blockText{$this->_block[2]}-- $blockName --{$this->_block[3]}", $text);
				} else 
				{					
				
					foreach($blockarray[$realBlockName] as $blockId => $block) 
					{
						$temp = $blockText;
						foreach($block as $tag => $replace) 
						{
							if($tag != "_SUBBLOCKS_") 
							{
								$tag = $this->tag_start . $blockName . "." . $tag . $this->tag_end;
								$temp = str_replace($tag, $replace, $temp);
							}
						}
						if(isset($block["_SUBBLOCKS_"]))
						{
							$temp = $this->_parseBlocks($temp, $block["_SUBBLOCKS_"], $leaveout, ($blocklevel != ""? $blocklevel.".".$realBlockName : $blockName));
						}
						$blockTemp .= $temp;
					}
					$blockTemp = preg_replace("/{$this->_block[0]} .*? {$this->_block[1]}.*?{$this->_block[2]} .*? {$this->_block[3]}/i", "", $blockTemp);
					$text = preg_replace("/{$this->_block[0]} $blockName {$this->_block[1]}.*?{$this->_block[2]} $blockName {$this->_block[3]}/is", $blockTemp, $text);
				}
			
			} else 
			{
				$text = preg_replace("/{$this->_block[0]} $blockName {$this->_block[1]}.*?{$this->_block[2]} $blockName {$this->_block[3]}/is", "", $text);
				$text = preg_replace("/{$this->_block[0]} $blockName {$this->_block[1]}/i", "", $text);
			}
		}
		return $text;
	}
	
	/**
	* Parses the switches in the give text
	*
	* This function will parse the switches in the given text.
	* <b>This should only be called by Template::parse()</b>
	* 
	* @param 	string 	The text to parse
	* @access private
	*
	*/
	function _parseSwitch($text, $leaveout) 
	{
		while(preg_match("/{$this->_switch[0]} (.*?) {$this->_switch[1]}/i", $text, $match))
		{
			$switchName = $match[1];
			$switchText = "";		
			if(preg_match("/{$this->_switch[0]} $switchName {$this->_switch[1]}(.*?){$this->_switch[2]} $switchName {$this->_switch[4]}/is", $text, $match)) {
				
				if(isset($this->switches[$switchName]) && $this->switches[$switchName] && !in_array($switchName, $leaveout))
					$switchText = $match[1];
				else if(in_array($switchName, $leaveout)) 
				{
					$switchText = preg_replace(array(
						"/{$this->_switch[0]} /i",
						"/ {$this->_switch[1]}/i",
						"/{$this->_switch[2]} /i",
						"/ {$this->_switch[3]}/i"
					), array(
						"{$this->_switch[0]}-- ",
						" --{$this->_switch[1]}",
						"{$this->_switch[2]} ",
						" --{$this->_switch[3]}"
					), $match[1]);
					$switchText = "{$this->_switch[0]}-- $switchName --{$this->_switch[1]}$switchText{$this->_switch[2]}-- $switchName --{$this->_switch[3]}";
				} else 
					$switchText = "";
				$text = preg_replace("/{$this->_switch[0]} $switchName {$this->_switch[1]}.*?{$this->_switch[2]} $switchName {$this->_switch[3]}/is", $switchText, $text, 1);
				
			} else 
			{
				$text = preg_replace("/{$this->_switch[0]} $switchName {$this->_switch[1]}/i", "", $text, 1);					
			}
				
		}
		return $text;
	}

	/**
	* Parses the page tags in the given text.
	*
	* This function will parse the page tags in the given text.
	* <b>This should only be called by Template::parse()</b>
	*
	* @param 	string 	The text to parse
	* @access private
	*/
	function _parsePageTags($text, $leaveout) 
	{
		if(sizeof($this->tags) > 0) 
		{
			foreach($this->tags as $tag => $replace) 
			{
			 	if(!in_array(trim($tag), $leaveout))
			 	{
					$tag = $this->tag_start . $tag . $this->tag_end;
					$text = str_replace($tag, $replace, $text);
			 	}
			}
		}
		return $text;
	}
	
	/**
	 * caching function. Still in developing stage and can change without a warning
	 */
	
	/**
	 * An array contining all the input Template::registerChace as gotten
	 *
	 * @var Array
	 * @access Private
	 */
	var $_caches = array();
	
	/**
	 * The directory where we'll save the cached templates.
	 * Set by Template::setCacheDir
	 *
	 * @var String
	 */
	var $_cacheDir = "";
	
	/**
	 * Register a template for caching. When the parsing function is called with the
	 * given file alias the output will be stored in a .tpl file for later use
	 *
	 * @param unknown_type $filealias
	 * @param unknown_type $cachename
	 * @param unknown_type $timeout
	 * @param unknown_type $leaveout
	 */
	function registerCache($filealias, $cachename, $timeout, $vars, $leaveout)
	{
		$this->_caches[$filealias] = array("name"=>$cachename, "timeout"=>$timeout, "vars"=>$vars, "leaveout"=>$leaveout);
	}
	
	/**
	 * Sets the cache directory. Returns false on failure.
	 *
	 * @param String $dir Directory which we'll use
	 * @return Boolean Success
	 */
	function setCacheDir($dir)
	{
		if(is_dir($dir)) 
		{
			$this->_cacheDir = $dir;
			return true;
		} else {
			if(mkdir($dir, 0777)) 
			{
				$this->_cacheDir = $dir;
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Attempts to generate a cache using the given name.
	 * <b>Should only be called by Template::parse!</b>
	 *
	 * @param String $cachename The name of the cache
	 * @param String $filealias The name of the template file we're parsing
	 * @param Boolean $breaktag Should we use a break tag?
	 * @param Boolean $inFrontOf Should be parse infront of the break tag?
	 */
	function _generateCache($cachename, $filealias, $breakTag, $inFrontOf)
	{		
		$leaveout = &$this->_caches[$filealias]["leaveout"];
		$variables = &$this->_caches[$filealias]["vars"];
		
		$cacheText = $this->parse($filealias, $breakTag, $inFrontOf, true, $leaveout);
		
		$timeout = time() + ($this->_caches[$filealias]["timeout"] * 60);
		$header .= "Timeout: $timeout\n";
		$header .= "TemplateSize: ".filesize($this->files[$filealias])."\n";
		foreach($variables as $var => $value) 
		{
			$header .= "$var: $value\n";
		}
		$header .= "\n";
		
		$file = fopen($this->_cacheDir."$cachename.tpl", "w");
		fwrite($file, $header.$cacheText);
		fclose($file);
	}
	
	/**
	 * Checks if the given cache file exists and if the stats in the file
	 * is valid this functions returns true, if not the cache file is deleted
	 *
	 * @param String $filealias The file alias
	 * @return boolean Valid file
	 */
	function hasCache($filealias)
	{
		$cache = $this->_caches[$filealias];
		$cacheName = $cache["name"];
		if(is_file($this->_cacheDir.$cacheName.".tpl") && is_readable($this->_cacheDir.$cacheName.".tpl"))
		{
			$file = file($this->_cacheDir.$cacheName.".tpl");
			foreach($file as $line)
			{
				if(trim($line) == "")
					break;
				if(preg_match("/Timeout: (.+)/", $line, $match))
				{
					if($match[1] < time()) 
					{
						$this->delCache($cacheName);
						return false;
					}
				} else if(preg_match("/TemplateSize: (.+)/", $line, $match))
				{
					if(filesize($this->files[$filealias]) != $match[1]) 
					{
						$this->delCache($cacheName);
						return false;
					}
				} else if(preg_match("/(.*?): (.+)/", $line, $match)) 
				{
					if(!isset($cache["vars"][$match[1]]) || $cache["vars"][$match[1]] != $match[2])
					{
						$this->delCache($cacheName);
						return false;
					}
				}
			}
			return true;
		}
		return false;
	}
	
	/**
	 * Parses the given cache file using the normal functions
	 *
	 * @param String $filealias The file alias
	 * @return String Parsed template
	 */
	function parseCache($filealias)
	{
		$cache = $this->_caches[$filealias];
		$cacheName = $cache["name"];
		if(is_file($this->_cacheDir.$cacheName.".tpl") && is_readable($this->_cacheDir.$cacheName.".tpl"))
		{
			$text = file($this->_cacheDir.$cacheName.".tpl");
			foreach($text as $lineno => $line) 
			{
				if(trim($line) == "")
				{
					unset($lineno);
					foreach($text as $lineno => $line)
					{
						if(trim($line) == "")
						{
							unset($text[$lineno]);
						}
					}
					break;
				}
				unset($text[$lineno]);	
			}
			$text = implode("", $text);
			$text = preg_replace(array(
				"/{$this->_block[0]}-- /i",
				"/ --{$this->_block[1]}/i",
				"/{$this->_block[2]}-- /i",
				"/ --{$this->_block[3]}/i"
			), array(
				"{$this->_block[0]} ",
				" {$this->_block[1]}",
				"{$this->_block[2]} ",
				" {$this->_block[3]}"),
			$text);
			$text = preg_replace(array(
				"/{$this->_switch[0]}-- /i",
				"/ --{$this->_switch[1]}/i",
				"/{$this->_switch[2]}-- /i",
				"/ --{$this->_switch[3]}/i"
			), array(
				"{$this->_switch[0]} ",
				" {$this->_switch[1]}",
				"{$this->_switch[2]} ",
				" {$this->_switch[3]}"
			), $text);
			$text = $this->parse($text, false, false, true);
			return $text;
		}
	}
	
	/**
	 * Deletes the given cache file
	 *
	 * @param String $cacheName The name of the cache file
	 */
	function delCache($cacheName)
	{
		if(is_file($this->_cacheDir.$cacheName.".tpl"))
		{
			unlink($this->_cacheDir.$cacheName.".tpl");
		}
	}
}
?>